/*=============================================================================
*
*   Copyright (C) 2020 All rights reserved.
*
*   Filename:		data_extractor.hpp
*
*   Author: Wang Zhecheng - wangzhecheng@yeah.net
*
*   Date: 2020-03-20 00:30
*
*   Last Editors: Wang Zhecheng - wangzhecheng@yeah.net
*
*   Last modified:	2020-03-25 12:05
*
*   Description:
*
=============================================================================*/
#ifndef DATA_EXTRACTOR_H
#define DATA_EXTRACTOR_H

#include "../model/model.hpp"
#include <vector>

namespace geoxfem {
namespace io {

template <typename Model>
struct DataExtractor : util::ObserverInterface<Model> {
  using dof_key = std::pair<size_t, std::string>;

  struct DofKeyHasher { /*{{{*/
    size_t operator()(const dof_key& key) const {
      return (std::hash<size_t>{}(key.first)
              ^ (std::hash<std::string>{}(key.second) << 1) >> 1);
    }
  };                   /*}}}*/
  struct DofKeyEqual { /*{{{*/
    bool operator()(const dof_key& lhs, const dof_key& rhs) const {
      return lhs.first == rhs.first && lhs.second == rhs.second;
    }
  }; /*}}}*/

  using Shape = typename Model::Shape;
  using Gauss = typename Shape::Gauss;
  using Dof   = typename model::Dof;

  void clear() { /*{{{*/
    m_list_dof_data.clear();
    /* m_list_cell_index.clear(); */
    return;
  } /*}}}*/

  void append(const size_t& v_id, const std::string& dof_name) { /*{{{*/
    m_list_dof_data.emplace(std::make_pair(v_id, dof_name),
                            std::vector<std::tuple<_real, _real>>{});
    return;
  } /*}}}*/

  void plotDofData() const { /*{{{*/
    for(auto data : m_list_dof_data) {
      std::string title = std::string("Vertex #")
                          + std::to_string(data.first.first) + "-"
                          + data.first.second;
      Gnuplot gp;
      gp << "plot '-' w l title '" + title + "'\n";
      gp.send1d(data.second);
    }
    return;
  } /*}}}*/

  void append(const size_t&) {}

  virtual void update(const Model* model) { /*{{{*/
    _real time = model->getTime();
    for(auto& it : m_list_dof_data) {
      size_t      v_id     = it.first.first;
      std::string dof_name = it.first.second;
      auto dof = model->getMesh()->getVertex(v_id)->toVertex().getDof(dof_name);
      it.second.emplace_back(std::make_tuple(time, dof->getCurrValue()));
    }
    return;
  } /*}}}*/

  std::vector<std::tuple<_real, _real>>
  getDataOnDof(const size_t& v_id, const std::string& dof_name) const { /*{{{*/
    return m_list_dof_data.at({v_id, dof_name});
  } /*}}}*/

private:
  std::unordered_map<dof_key,
                     std::vector<std::tuple<_real, _real>>,
                     DofKeyHasher,
                     DofKeyEqual>
    m_list_dof_data;
  /* std::map<Gauss*, */
  /*          std::map<_real, util::iRecord<typename Model::DimensionType>>> */
  /*   m_list_cell_index; */
};

}  // namespace io
}  // namespace geoxfem

#endif /* DATA_EXTRACTOR_H */
